#############################################################################
# Copyright (c) 2009 Dmitry Grushin <dgrushin@gmail.com>.
#
# This file is part of clupower (cluster power manager utility)
#
# Clupower is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Clupower is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Clupower.  If not, see <http://www.gnu.org/licenses/>.
#
# Contributors:
#    Dmitry Grushin <dgrushin@gmail.com> - initial API and implementation
#############################################################################

require 'settings'
require 'singleton'
require 'logger'
require 'commands'

class CPMonitor
  include Singleton

  def initialize
    CPMonitor.rotate_log(Settings.instance.get_main_log_path)
    CPMonitor.rotate_log(Settings.instance.get_stats_file_path)

    file = open(Settings.instance.get_main_log_path, 'w')
    file.sync = true
    @log = Logger.new(file)
    @log.datetime_format = "%m-%d %H:%M:%S"
    @stats = open(Settings.instance.get_stats_file_path, 'w')
    @stats.sync = true
    @wait_time_tab = {}
    @off_nodes = 0
    @size = Cmd.instance.get_cluster_size
    @switch_count_total = 0
    @switch_count = {}
  rescue CPRuntimeError => e
    raise CPInitError.new.set_cause(e)
  end

  def log_node_on(node)
    @log.info("node on #{node}")
    @switch_count_total += 1
    node_count = @switch_count[node]
    @switch_count[node] = node_count != nil ? node_count + 1 : 1
  end

  def log_node_off(node)
    @log.info("node off #{node}")
  end

  def log_job_release(job, nodes)
    @log.info("Nodes #{nodes.join(",")} ready. Release job hold #{job}")
  end

  def log_error(message)
    @log.error(message)
  end

  def log_message(message)
    @log.info(message)
  end

  def log_job_wait_time(job, wtime)
    @wait_time_tab[job] = wtime
  end

  def print_stats_message(message)
    @stats.puts(";#{message}")
  end

  def print_stats
    # get average wait time
    avg_wait = 0
    max_wait = 0
    unless @wait_time_tab.empty?
      @wait_time_tab.each_pair do |k,v|
        avg_wait += v
        max_wait = v if v > max_wait
      end
      avg_wait = avg_wait / @wait_time_tab.size
    end
    #get maximum switch count
    swtmax = 0
    @switch_count.each_pair { |k,v| swtmax = v if v > swtmax }
    # print stats line to the file
    off_nodes = Cmd.instance.get_sleep_nodes.size
    log_message = { :avg_wait => avg_wait, :max_wait => max_wait,
      :off_nodes => off_nodes, :on_nodes => @size - off_nodes,
      :switch_total => @switch_count_total, :switch_max => swtmax }

    print_stats_line(log_message)

    # clear wait time values
    @wait_time_tab.clear
  end

  # Call this when the server is finished. This will flush stats buffer.
  def finish
  end

  # Renames existing log file
  def CPMonitor.rotate_log(name)
    num = 0
    Dir.glob("#{name}.*") do |file|
      num = $1.to_i if file =~ /.*\.(\d+)/ && num < $1.to_i
    end
    num += 1
    File.rename(name, "#{name}.#{num}") if File.exist?(name)
  end

  private

  def print_stats_line(stats)
    @stats.puts("#{Time.now.strftime("%d:%m:%Y:%H:%M:%S")},await=#{stats[:avg_wait]},maxwait=#{stats[:max_wait]}," +
        "off=#{stats[:off_nodes]},on=#{stats[:on_nodes]}," +
        "swtcount=#{stats[:switch_total]},swtmax=#{stats[:switch_max]}")
  end
end